package org.joe.chinachess.core;

import lombok.AccessLevel;
import lombok.Data;
import lombok.Getter;
import lombok.extern.java.Log;
import org.joe.chinachess.core.po.Chess;
import org.joe.chinachess.core.rules.ChessRule;
import org.joe.chinachess.core.rules.PaoRule;
import org.joe.chinachess.utils.Constants;
import org.joe.chinachess.utils.Point;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Joe
 */
@Data
@Log
public class ChessBoard implements Cloneable {

    /**
     * 棋盘存放棋子的数据模型
     */
    private Map<Point, Chess> pointChessMap;// 存放整个棋盘的数据模型

    /**
     * 基本数据
     */
    @Getter(AccessLevel.PRIVATE)
    private List<Chess> chessList;

    /**
     * 选中的棋子，可能要做移动
     */
    private Point selectPoint;// 被选中且将要移动的棋子

    /**
     * 最近一次移动的起始位置
     */
    private Point startPoint;

    /**
     * 当前步数，如果=0，表示还未开始。
     */
    private int step;

    /**
     * 先手颜色。a默认为先手。
     */
    private int aColor;

    /**
     * 对方颜色。
     */
    private int bColor;


    public ChessBoard() {
        this.chessList = ChessFactory.initChesses();
        this.pointChessMap = initChessesDataModel();
        this.startPoint = null;
    }

    /**
     * 更新指针对应棋子的状态
     *
     * @param point
     * @param status
     */
    public void updateStatus(Point point, int status) {
        if (point == null) {
            return;
        }
        Chess c = this.pointChessMap.get(point);
        if (c == null) {
            return;
        }
        c.setState(status);
    }

    private Map<Point, Chess> initChessesDataModel() {
        Map<Point, Chess> chessModel = new HashMap<>();
        int n = 0;
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 4; j++) {
                Chess chess = this.chessList.get(n++);
                chessModel.put(new Point(i, j), chess);
            }
        }
        return chessModel;
    }


    public void operation(Point p) {
        if (p == null) {
            return;
        }
        log.info(String.format("位置P：%s  对应棋子: %s", p, pointChessMap.get(p)));
        //第一步操作，决定红黑双方。
        if (step == 0) {
            firstOperation(p);
            return;
        }
        //是否是翻子操作
        if (isOpenChess(p)) {
            updateStatus(p, Constants.Status.UNSELECT);
            updateStatus(this.startPoint, Constants.Status.UNSELECT);
            this.startPoint = null;
            this.step++;
            return;
        }
        //是否是选子操作
        if (isSelectOneChess(p)) {
            updateStatus(this.startPoint, Constants.Status.UNSELECT);
            updateStatus(p, Constants.Status.SELECT);
            this.startPoint = p;
            return;
        }

        //是否是移动操作
        if (isMoveChess(this.startPoint, p)) {
            updatePoint(this.startPoint, p);
            updateStatus(p, Constants.Status.UNSELECT);
            this.startPoint = null;
            this.step++;
            return;
        }

        //是否是兑子操作
        if (isExchangeChess(this.startPoint, p)) {
            updateDead(this.startPoint);
            updateDead(p);
            this.startPoint = null;
            this.step++;
            return;
        }

        //是否是吃子操作
        if (isEatChess(this.startPoint, p)) {
            updateDead(p);
            updatePoint(this.startPoint, p);
            updateStatus(p, Constants.Status.UNSELECT);
            this.step++;
            return;
        }
    }

    private boolean isOpenChess(Point p) {
        Chess chess = this.pointChessMap.get(p);
        return chess != null && Constants.Status.UNKNOW == chess.getState();
    }

    /**
     * 将指定位置的棋子设为死亡状态
     *
     * @param point
     */
    private void updateDead(Point point) {
        if (point == null) {
            return;
        }
        pointChessMap.put(point, null);
    }

    /**
     * 将startPoint的棋子，移动到endPoint的位置
     *
     * @param startPoint
     * @param endPoint
     */
    private void updatePoint(Point startPoint, Point endPoint) {
        if (startPoint == null || endPoint == null) {
            return;
        }
        Chess chess = pointChessMap.get(startPoint);
        pointChessMap.put(startPoint, null);
        pointChessMap.put(endPoint, chess);
    }


    /**
     * 开局操作。
     *
     * @param p
     */
    private void firstOperation(Point p) {
        Chess chess = pointChessMap.get(p);
        this.aColor = chess.getColor();
        this.bColor = chess.getColor() == Constants.Color.BLACK ? Constants.Color.RED : Constants.Color.BLACK;
        updateStatus(p, Constants.Status.UNSELECT);
        this.step++;
    }

    /**
     * 是否是移动操作：
     * 1，startPoint 不能为null，对应的棋子也不能为null
     * 2，endPoint 不能为null，对应的棋子必须为null
     *
     * @param startPoint
     * @param endPoint
     * @return
     */
    private boolean isMoveChess(Point startPoint, Point endPoint) {
        if (startPoint == null || pointChessMap.get(startPoint) == null) {
            return false;
        }
        if (endPoint == null || pointChessMap.get(endPoint) != null) {
            return false;
        }
        return true;
    }

    /**
     * 首先，选子操作步数不加1。是否可以选择一个棋子：
     * 1，本次选中的位置有棋子
     * 2，本次选中的是自己的棋子
     *
     * @param p
     * @return
     */
    private boolean isSelectOneChess(Point p) {
        Chess chess = pointChessMap.get(p);
        if (chess == null) {
            return false;
        }
        int op = step % 2 == 0 ? aColor : bColor;//获取当前操作方的颜色。
        if (op != chess.getColor()) {
            return false;
        }
        return true;
    }

    /**
     * 兑子的条件：
     * 1，两个棋子属于双方
     * 2，起始的棋子比目标棋子要相等
     *
     * @return
     */
    private boolean isExchangeChess(Point start, Point end) {
        return compareTwoPoint(start, end) == 0;
    }

    /**
     * 兑子的条件：
     * 1，两个棋子属于双方
     * 2，起始的棋子比目标棋子要大
     *
     * @return
     */
    private boolean isEatChess(Point start, Point end) {
        Chess chess = pointChessMap.get(start);

        if (chess.getChess() == Constants.ChessPiece.P) {
            ChessRule chessRule = new PaoRule();
            try {
                return chessRule.isCanEat(start, end, this);
            } catch (Exception e) {
                return false;
            }
        }
        int result = compareTwoPoint(start, end);
        return (result > 0 && result != (Constants.ChessPiece.J - Constants.ChessPiece.Z) || result == (Constants.ChessPiece.Z - Constants.ChessPiece.J));
    }

    /**
     * 比较双方两个棋子的大小
     * 1，两个指针不能为null
     * 2，两个指针指向的棋子不能为null
     * 3，两个棋子不能同样的颜色，同样的颜色表示属于同一方
     * 4，返回<code>Constants.ERROR_CODE<code/>表示不能比较，或者无需比较
     *
     * @param startPoint
     * @param endPoint
     * @return
     */
    private int compareTwoPoint(Point startPoint, Point endPoint) {
        if (startPoint == null) {
            return Constants.ERROR_CODE;
        }
        if (endPoint == null) {
            return Constants.ERROR_CODE;
        }

        Chess start = pointChessMap.get(startPoint);
        if (start == null) {
            return Constants.ERROR_CODE;
        }

        Chess target = pointChessMap.get(endPoint);
        if (target == null) {
            return Constants.ERROR_CODE;
        }

        if (start.getColor() == target.getColor()) {
            return Constants.ERROR_CODE;
        }
        return start.getChess() - target.getChess();
    }
}
